package com.chuangke.dynamic.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.toolkit.SqlRunner;
import com.chuangke.common.constant.Masks;
import com.chuangke.common.exception.ChuangkeException;
import com.chuangke.common.sqlparser.CbExp;
import com.chuangke.common.sqlparser.CkPageQuery;
import com.chuangke.common.sqlparser.SqlUtils;
import com.chuangke.dev.model.SysModelColumn;
import com.chuangke.dev.model.SysModelTable;
import com.chuangke.dev.service.SysModelColumnService;
import com.chuangke.dev.service.SysModelTableService;
import com.chuangke.dynamic.service.DynamicTableService;
import com.chuangke.framework.model.Record;
import org.apache.commons.collections.CollectionUtils;
import org.apache.ibatis.logging.Log;
import org.apache.ibatis.logging.LogFactory;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Service
public class DynamicTableServiceImpl implements DynamicTableService {

	protected final Log log = LogFactory.getLog(getClass());

	@Autowired
	private SqlSessionFactory sqlSessionFactory;

	@Autowired
	private SysModelTableService sysModelTableService;
	@Autowired
	private SysModelColumnService sysModelColumnService;
	
	private SysModelTable getModelTable(String tableId) {
		SysModelTable modelTable = sysModelTableService.getById(tableId) ;
		
		if(modelTable==null) {
			throw new ChuangkeException("未找到元数据信息") ;
		}
		return modelTable ;
	}
	
	@Override
	public List<Record> list(String tableId, CbExp cbExp) {
		SysModelTable modelTable = getModelTable(tableId) ;
		
		Map<String, Object> params = new HashMap<>();

		String sql = SqlUtils.getSelectSql(modelTable.getTableName(), cbExp, params);
		
		log.warn(sql);
		
		params.put("exeSql", sql);
		List<Map<String, Object>> result = SqlRunner.db().selectList(sql);
		if (CollectionUtils.isEmpty(result)) {
			return null;
		}

		List<Record> recordList = new ArrayList<>(result.size());
		result.forEach(d -> recordList.add(new Record(d)));

		return recordList;
	}

	@Override
	public IPage<Record> page(String tableId, CkPageQuery<Record> query) {
		SysModelTable modelTable = getModelTable(tableId) ;
		
		Map<String, Object> params = new HashMap<>();

		String sql = SqlUtils.getSelectSql(modelTable.getTableName(), query.getCbExp(), params);
		
		log.warn(sql);
		
//		params.put("exeSql", sql);
//		IPageresult = SqlRunner.db().selectPage(query.getPage(), sql, null);
//		if (CollectionUtils.isEmpty(result.getRecords())) {
//			return null;
//		}
//
//		List<Record> recordList = new ArrayList<>(result.getRecords().size());
//		result.getRecords().forEach(d -> recordList.add(new Record((Map<String, Object>) d)));
//		result.setRecords(recordList);

//		return result;
		return null ;
	}

	@Override
	@Transactional(rollbackFor = Throwable.class)
	public void delete(String tableId, CbExp cbExp) {
		SysModelTable modelTable = getModelTable(tableId) ;
		
		Map<String, Object> params = new HashMap<>();

		String sql = SqlUtils.getDeleteSql(modelTable.getTableName(), cbExp, params);
		
		log.warn(sql);
		
		params.put("exeSql", sql);

		SqlRunner.db().delete(sql);
	}

//	@Override
//	public void insert(String table, List<Record> records) {
//		if (CollectionUtils.isEmpty(records)) {
//			return;
//		}
//
//		List<SysModelColumn> columns = sysModelColumnService
//				.list(new QueryWrapper<SysModelColumn>().lambda().eq(SysModelColumn::getTableId, 94));
//
//		List<String> cols = columns.stream().map(SysModelColumn::getColumnName).collect(Collectors.toList());
//		String sql = SqlUtils.getInsertSql(table, cols.toArray(new String[cols.size()]));
//
//		Class<?> entityClass = (Class<?>) ReflectionKit.getSuperClassGenericType(this.getClass(),
//				DynamicTableServiceImpl.class, 1);
//		
//		List<Map<String,Object>> list = records.stream().map(record -> record.getColumns()).collect(Collectors.toList());
//
//		SqlHelper.executeBatch(SqlExecutorMapper.class, this.log, list, 100,
//				(sqlSession, entity) -> sqlSession.insert(sql, entity));
//		Map<String,Object> map = records.get(0).getColumns() ;
//		map.put("exeSql", sql);
//		sqlExecutorMapper.insert(map ) ;
//	}

	@Override
	@Transactional(rollbackFor = Throwable.class)
	public void insert(String tableId, List<Record> records) {
		if (CollectionUtils.isEmpty(records)) {
			return;
		}
		
		SysModelTable modelTable = getModelTable(tableId) ;

		List<SysModelColumn> columns = sysModelColumnService
				.list(new QueryWrapper<SysModelColumn>().lambda().eq(SysModelColumn::getTableId, tableId));

		List<String> cols = columns.stream().map(SysModelColumn::getColumnName).toList();
		String[] colarr = cols.toArray(new String[0]);
		String sql = SqlUtils.getInsertSql1(modelTable.getTableName(), colarr);
		
		log.warn(sql);

		SqlSession session = sqlSessionFactory.openSession();
		Connection conn = session.getConnection();
		PreparedStatement pstmt = null;
		try {
			pstmt = conn.prepareStatement(sql);
			conn.setAutoCommit(false);

			int size = records.size();
			for (int i = 0; i < records.size(); i++) {
				for (int j = 1; j <= colarr.length; j++) {
					pstmt.setObject(j, records.get(i).getObject(colarr[j - 1]));//StringUtils.camelize(colarr[j - 1])
				}

				pstmt.addBatch();

				if ((i != 0 && i % 200 == 0) || i == size - 1) {
					pstmt.executeBatch();
					pstmt.clearBatch();
				}
			}

			conn.commit();
		} catch (Exception ex) {
			throw new ChuangkeException(ex) ;
		} finally {
			try {
				pstmt.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}
//			try {
//				conn.close();
//			} catch (SQLException e) {
//				e.printStackTrace();
//			}
		}
	}

	@Override
	@Transactional(rollbackFor = Throwable.class)
	public void update(String tableId, Map<String, Object> updateData, CbExp cbExp) {
		SysModelTable modelTable = getModelTable(tableId) ;
		
		String[] updateCols = updateData.keySet().toArray(new String[0]);
		String sql = SqlUtils.getUpdateSql(modelTable.getTableName(), cbExp, updateData, updateCols);
		
		log.warn(sql);

		updateData.put("exeSql", sql);

		SqlRunner.db().update(sql);
	}

	@Override
	@Transactional(rollbackFor = Throwable.class)
	public void updateByPk(String tableId, List<Record> records) {
		List<SysModelColumn> columns = sysModelColumnService
				.list(new QueryWrapper<SysModelColumn>().lambda().eq(SysModelColumn::getTableId, tableId));

		List<String> updateCols = columns.stream().filter(col -> Masks.BOOL_NO.equals(col.getIsPk()))
				.map(SysModelColumn::getColumnName).toList();
		String[] updateColarr = updateCols.toArray(new String[0]);

		List<String> pkCols = columns.stream().filter(col -> Masks.BOOL_YES.equals(col.getIsPk()))
				.map(SysModelColumn::getColumnName).toList();
		String[] pkColarr = updateCols.toArray(new String[pkCols.size()]);
		
		SysModelTable modelTable = getModelTable(tableId) ;

		String sql = SqlUtils.getBatchUpdateSql(modelTable.getTableName(), updateColarr, pkColarr);
		
		log.warn(sql);

		SqlSession session = sqlSessionFactory.openSession();
		Connection conn = session.getConnection();
		PreparedStatement pstmt = null;
		try {
			pstmt = conn.prepareStatement(sql);
			conn.setAutoCommit(false);

			int size = records.size();
			for (int i = 0; i < records.size(); i++) {
				for (int j = 1; j <= updateColarr.length; j++) {
					pstmt.setObject(j, records.get(i).getObject(updateColarr[j - 1]));//StringUtils.camelize(updateColarr[j - 1])
				}
				for (int j = 1; j <= updateColarr.length; j++) {
					pstmt.setObject(j + updateColarr.length,
							records.get(i).getObject(updateColarr[j - 1]));//StringUtils.camelize(updateColarr[j - 1])
				}

				pstmt.addBatch();

				if ((i != 0 && i % 200 == 0) || i == size - 1) {
					pstmt.executeBatch();
					pstmt.clearBatch();
				}
			}

			conn.commit();
		} catch (Exception ex) {
			throw new ChuangkeException(ex) ;
		} finally {
			try {
				pstmt.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}
//			try {
//				conn.close();
//			} catch (SQLException e) {
//				e.printStackTrace();
//			}
		}
	}

}
